<?php
namespace Core;

class BuildProtocol
{

    public $raw;

    private $_map = array(
        '1000' => 'sendHeartPack',
        '1001' => 'sendTokenPack',
        '1002' => 'setHeartSpace',
        '1003' => 'getHeartSpace',
        '1004' => 'setRestartReset',
        '1005' => 'setDomainPort',
        '1006' => 'getDomainPort',
        '1007' => 'setIpPort',
        '1008' => 'getIpPort',
        '1009' => 'setPolicy',
        '100A' => 'getPolicy',
        '100B' => 'offlineTime',
        '100C' => 'setWorkMode',
        '100D' => 'setFilter',
        '100E' => 'getFilter',
        '1010' => 'setFirmware',
        '2010' => 'setFirmware',
        '2011' => 'upgradeConfirm',
        '2012' => 'setFirmwareData',
        '3000' => 'setDataSpace',
        '3001' => 'getDataSpace',
        '3002' => 'setDataUploadWay',
        '3003' => 'getDataUploadWay',
        '5000' => 'getDevice',
        '5001' => 'getDeviceStatus',
        '5002' => 'getDeviceLocal',
        '5003' => 'getDeviceFault',
        '5103' => 'getDeviceFaultReport',
        'a102' => 'canelUrgentStatus',
    );

    //指令的发送和响应消息类型对照表，键值格式：响应消息类型=>发送消息类型
    public $correspondingMap = [
        //基础管理类
        '2002'=>'1002',//设置心跳间隔
        '2003'=>'1003',//获取心跳间隔
        '2004'=>'1004',//设置设备启动和复位
        '2005'=>'1005',//设置服务器域名和端口
        '2006'=>'1006',//获取服务器域名和端口
        '2007'=>'1007',//设置服务器的IP和端口
        '2008'=>'1008',//获取服务器的IP和端口
        '2009'=>'1009',//设置服务器地址策略（ip还是域名）
        '200A'=>'100A',//获取服务器地址策略
        '200B'=>'100B',//设置终端下线时间
        '2010'=>'1010',//设备固件升级
        '2011'=>'1011',//设备固件升级数据
        //数据管理类
        '4000'=>'3000',//设置数据上报间隔
        '4001'=>'3001',//获取数据上报间隔
        '4002'=>'3002',//设置数据上传方式
        '4003'=>'3003',//获取数据上传方式
        //设备信息类
        '6000'=>'5000',//获取设备信息
        '6001'=>'5001',//获取设备状态
        '6002'=>'5002',//获取设备位置信息
        '6003'=>'5003',//获取设备故障信息
        '6103'=>'5103',//设备故障信息上报
    ];

    public function parsein($array)
    {
        $type = $array['type'];
        if(!isset($this->_map[$type])) return false;
        $method = $this->_map[$type];
        return $this->$method($array);

    }

    /**
     * 发送心跳包
     * 无消息体和消息尾
     */
    public function sendHeartPack(){
        return $this->pushhex("10 00 00 04");
    }

    /**
     * 发送令牌包
     * 无消息尾,消息体是时间戳
     */
    public function sendTokenPack($array){
        $token = dechex($array['token']);
        $string = $this->splitMsgBody($token,1);
        return $this->pushhex("10 01 00 08 ".$string);
    }

    /**
     * 设置心跳间隔
     */
    function setHeartSpace($array)
    {
        $token = $array['token'];
        $space = $array['data']['space'];
        $string = sprintf("%04x",$space);
        $str="1002000E".$string."0000000000000000";
        return $this->encryption($str, $token);
    }

    /**
     * 获取心跳间隔
     */
    function getHeartSpace($array){
        $token = $array['token'];
        $string = "1003000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置重启和复位
     */
    function setRestartReset($array){
        $token = $array['token'];
        $status = $array['data']['status'];
        $string = sprintf("%02x",$status);
        $str = "1004000D".$string."0000000000000000";
        return $this->encryption($str,$token);
    }


    /**
     * 设置域名和端口
     */
    function setDomainPort($array){
        $token=$array['token'];

        $dataDomain = $array['data']['dataDomain'];
        $dataPort = $array['data']['dataPort'];
        $cfgDomain = $array['data']['cfgDomain'];
        $cfgPort = $array['data']['cfgPort'];

        $dataDomainArr = $this->strToHex($dataDomain);
        $dataPort = sprintf("%04x", $dataPort);

        $cfgDomainArr = $this->strToHex($cfgDomain);
        $cfgPort = sprintf("%04x", $cfgPort);

        $dataDomainLen = strlen($dataDomainArr) / 2;
        $cfgDomainLen = strlen($cfgDomainArr) / 2;

        $allLen = sprintf("%04x", (18 + $dataDomainLen + $cfgDomainLen));  //长度

        $dataBody = sprintf("%02x", $dataDomainLen) . $dataDomainArr . $dataPort;
        $cfgBody = sprintf("%02x", $cfgDomainLen) . $cfgDomainArr . $cfgPort;

        $str = "1005" . $allLen . $cfgBody . $dataBody . "0000000000000000";
        return $this->encryption($str, $token);
    }

    /**
     * 获取域名和端口
     */
    function getDomainPort($array){
        $token = $array['token'];
        $string="1006000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置服务器ip和端口
     */
    function setIpPort($array) {
        $token = $array['token'];
        $dataip = sprintf("%08x", ip2long($array['data']['dataIP']));
        $cfgip = sprintf("%08x", ip2long($array['data']['cfgIP']));
        $dataport = sprintf("%04x", $array['data']['dataPort']);
        $cfgport = sprintf("%04x", $array['data']['cfgPort']);
        $string = "10070018" . $cfgip . $cfgport . $dataip . $dataport . "0000000000000000";
        return $this->encryption($string, $token);
    }

    /**
     * 获取服务器ip和端口
     */
    function getIpPort($array){
        $token=$array['token'];
        $string="1008000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置服务地址策略
     */
    function setPolicy($array){
        $data = sprintf("%02x", $array['data']['data']);
        $cfg = sprintf("%02x", $array['data']['cfg']);
        $token = $array['token'];
        $string="1009000E{$data}{$cfg}0000000000000000";
        echo $string . PHP_EOL;
        return $this->encryption($string,$token);
    }

    /**
     * 获取服务器策略地址
     */
    function getPolicy($array){
        $token=$array['token'];
        $string="100A000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置终端下线时间
     */
    function offlineTime($array){
        $token = $array['token'];
        $cfgDataOffline = sprintf("%04x", $array['data']['cfg']);
        $datDataOffline = sprintf("%04x", $array['data']['data']);
        $string="100B0010".$cfgDataOffline.$datDataOffline."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置终端工作模式
     * @param $array
     * @return array|bool|string
     */
    public function setWorkMode($array)
    {
        $token = $array['token'];
        $mode = sprintf("%02x", $array['data']['mode']);
        $string="100C000D".$mode."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置过滤策略
     * @param $array
     * @return array|bool|string
     */
    public function setFilter($array)
    {
        $token = $array['token'];
        $type = sprintf("%02x", $array['data']['type']);
        $macs = $array['data']['macs'];
        $maclen = sprintf("%02x", (strlen($macs) / 2));
        $len = sprintf("%04x", (14 + hexdec($maclen)));
        $string="100D".$len.$type.$maclen.$macs."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取过滤策略
     * @param $array
     */
    public function getFilter($array)
    {
        $token = $array['token'];
        $type = sprintf("%02x", $array['data']['type']);
        $string="100E000D".$type."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置固件升级
     */
    public function setFirmware($array){
        $token = $array['token'];
        $ip = sprintf("%08x", ip2long($array['data']['ip']));
        $port = sprintf("%04x", $array['data']['port']);
        $firmware = str_pad(str2ascii($array['data']['frimware']), 60, 0, STR_PAD_LEFT);
        $size = sprintf("%08x", $array['data']['size']);
        $hardver = sprintf("%02x", $array['data']['hardver']);
        $softver = sprintf("%02x", $array['data']['softver']);
        $string="10100036".$ip.$port.$firmware.$size.$softver.$hardver."0000000000000000";
        return $this->encryption($string, $token);
    }

    /**
     * 设置固件升级数据
     */
    function upgradeConfirm($array){
        $status = sprintf("%02x", $array['data']['status']);
        $string = "20110006" . $status;
        $verify = sumVerify(hexCalcSum($string));
        return hex2bin($string.$verify);
    }

    /**
     * 获取升级数据包
     * @param $array
     * @return string
     */
    public function getFirmwareData($array)
    {
        $packlength = $array['data']['packlength'];
        $file = $array['data']['file'];
        $packnum =  $array['data']['packnum'];
        $offset = ($packnum - 1) * $packlength;
        //读取文件内容
        $fp = fopen($file, 'r');
        fseek($fp, $offset, SEEK_CUR);
        $data = fread($fp, $packlength);
        fclose($fp);
        $msglen = sprintf("%04x", (strlen($data) + 5));
        $string = "2012" . $msglen . bin2hex($data);
        $verify = sumVerify(hexCalcSum($msglen));
        return hex2bin($string . $verify);
    }

    /**
     * 设置上报数据间隔
     */
    function setDataSpace($array){
        $token = $array['token'];
        $AP = sprintf("%04x",$array['data']['ap']);
        $STA = sprintf("%04x",$array['data']['sta']);
        $RF = sprintf("%04x",$array['data']['rf']);
        $string="30000012".$AP.$STA.$RF."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取上报数据间隔
     */
    function getDataSpace($array){
        $token=$array['token'];
        $string="3001000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 设置数据上传方式
     */
    function setDataUploadWay($array){
        $token=$array['token'];
        $status=$array['data']['status'];
        $status=sprintf("%02x",$status);
        $string="3002000D".$status."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取数据上传方式
     */
    function getDataUploadWay($array){
        $token=$array['token'];
        $string="3003000C0000000000000000";
        return $this->encryption($string,$token);
    }


    /**
     * 获取设备信息
     */
    function getDevice($array){
        $token=$array['token'];
        $string="5000000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取设备状态
     */
    function getDeviceStatus($array){
        $token=$array['token'];
        $string="5001000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取设备位置信息
     */
    function getDeviceLocal($array){
        $token=$array['token'];
        $string="5002000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取设备故障信息
     */
    function getDeviceFault($array){
        $token=$array['token'];
        $string="5003000C0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 获取设备故障信息上报
     */
    function getDeviceFaultReport($array){
        $token=$array['token'];
        $configServer=$array['data']['configServer'];
        $dataServer=$array['data']['dataServer'];
        $device=$array['data']['device'];
        $tem=$array['data']['tem'];
        $power=$array['data']['power'];
        $configServer=sprintf("%02x",$configServer);
        $dataServer=sprintf("%02x",$dataServer);
        $device=sprintf("%02x",$device);
        $tem=sprintf("%02x",$tem);
        $power=sprintf("%02x",$power);
        $string="60030011".$configServer.$dataServer.$device.$tem.$power."0000000000000000";
        return $this->encryption($string,$token);
    }

    /**
     * 上传RF数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A102
     *  @author qmx
     */
    public function canelUrgentStatus($array)
    {
        $token = $array['token'];
        $tagID = $array['tagID'];
        $string = "A1020014" . $tagID . "0000000000000000";
        return $this->encryption($string,$token);
    }



    //字符串转化为16进制
    function strToHex($string)
    {
        $hex="";
        for($i=0;$i<strlen($string);$i++)
            $hex.=dechex(ord($string[$i]));
        $hex=strtoupper($hex);
        return $hex;
    }

    function pushhex($string, $type = 0) {
        if ($type == 0) $this->raw = $string;
        $checksum = 0x0;
        $cmdStrArr = explode(' ',$string);
        for($i=0;$i<count($cmdStrArr);$i++){
            $cmdHex[] = chr(hexdec($cmdStrArr[$i]));
        }
        // $cmdHex[] = chr($checksum);
        $cmdHex=implode($cmdHex);
        return $cmdHex;
    }

    //16进制转化为字符串
    function hex2char($string){
        $str="";
        for($i=0;$i<strlen($string)-1;$i+=2){
            $str.=chr(hexdec($string[$i].$string[$i+1]));
        }
        return $str;
    }

    //消息体分割(2位一分割)
    function splitMsgBody($str,$type)
    {
        $data = implode(" ", str_split($str, 2));
        if($type == 1) {
            return $data;
        } else {
            $count = count($data);
            $data = array("count" => $count, "string" => $str);
            return $data;
        }

    }

    /**
     * 加密
     */
    function encryption($string,$token){
        $this->raw = $string;
        $data=pack("H*",$string);
        if (false == $md5type = config('server')['md5type'] ) {
            $md5type = 64;
        }
        $value=bin2hex(encrypt_data($data, $token, $md5type));
        $status=substr($value,0,2);   //截取前两个字符判断是否解密成功 16进制31（1 ascii）成功、30（0 ascii）失败
        if($status!="31") return false;
        $str=$this->pushhex($this->splitMsgBody(substr($value,2),1), 1);
        return $str;
    }
}